/*======================================================================
    A globalmem driver as an example of char device drivers  
   
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/kfifo.h>
#define GLOBALFIFO_SIZE	0x1000	/*全局fifo最大4K字节*/
#define FIFO_CLEAR 0x1  /*清0全局内存的长度*/
#define GLOBALFIFO_MAJOR 0    /*预设的globalfifo的主设备号*/
#define BLOCKSIZE      16   //预设每次读取的数据块大小
#define BUFSIZE        1024*1024  //预设定缓冲区大小为1m
static int globalfifo_major = GLOBALFIFO_MAJOR;
static int block_size = BLOCKSIZE;
static int buf_size   = BUFSIZE;

//this is kfifo segment




//end of kfifo segment
/*globalfifo设备结构体*/
struct globalfifo_dev                                     
{                                                        
  struct cdev cdev; /*cdev结构体*/                       
   
   struct semaphore sem; /*并发控制用的信号量*/           
   wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/ 
     // wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/
     spinlock_t mem_lock;  
     struct kfifo  *memfifo;
};

struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
  /*将设备结构体指针赋值给文件私有数据指针*/
  filp->private_data = globalfifo_devp;
  return 0;
}
/*文件释放函数*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
  return 0;
}



/*globalfifo读函数*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
  loff_t *ppos)
{
  int ret;
  struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
//  DECLARE_WAITQUEUE(wait,current);
  
  char  temp[block_size];
  if(down_interruptible(&dev->sem))
       return -ERESTARTSYS;
  
  while(!kfifo_len(dev->memfifo))
  {
       up(&dev->sem);//没有可以读的释放信号量
       if(filp->f_flags & O_NONBLOCK)
	    return -EAGAIN;
       printk("\n%s reading:going to sleep\n",current->comm);
       if(wait_event_interruptible(dev->r_wait,kfifo_len(dev->memfifo)))
          return -ERESTARTSYS;
       if(down_interruptible(&dev->sem))
       return -ERESTARTSYS;     
       
  }
  
  if(count > kfifo_len(dev->memfifo))
       count = kfifo_len(dev->memfifo);
  
  ret = kfifo_get(dev->memfifo,temp,count);
  if(copy_to_user(buf,temp,count))
     
  {
       ret = -EFAULT;
          
      
 }
  up(&dev->sem);
  
    return ret;
}


/*globalfifo写操作*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
  size_t count, loff_t *ppos)
{
  struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
 
  int ret;
  char temp[block_size];
  if(down_interruptible(&dev->sem))
       return -ERESTARTSYS;
  
  if( copy_from_user(temp,buf,count))
  {
       up(&dev->sem);
       return -EFAULT;
       
  }
 
  
   ret =kfifo_put(dev->memfifo,temp,count);
   up(&dev->sem);
   wake_up_interruptible(&dev->r_wait);
    return ret;
}


/*文件操作结构体*/
static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  // .ioctl = globalfifo_ioctl,
  // .poll = globalfifo_poll,
  .open = globalfifo_open,
  .release = globalfifo_release,
};

/*初始化并注册cdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
  int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*设备驱动模块加载函数*/
int globalfifo_init(void)
{
  int ret;
  dev_t devno = MKDEV(globalfifo_major, 0);

  /* 申请设备号*/
  if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "memfifo");
  else  /* 动态申请设备号 */
  {
    ret = alloc_chrdev_region(&devno, 0, 1, "memfifo");
    globalfifo_major = MAJOR(devno);
  }
  if (ret < 0)
    return ret;
  /* 动态申请设备结构体的内存*/
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if (!globalfifo_devp)    /*申请失败*/
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }

  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

    init_MUTEX(&globalfifo_devp->sem);   /*初始化信号量*/
   init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/
  // init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/
    spin_lock_init(&globalfifo_devp->mem_lock);
   //注意这里的自旋锁一定不要使用临时变量啊
  //spinlock_t mem_lock = SPIN_LOCK_UNLOCKED;
    
  globalfifo_devp->memfifo = kfifo_alloc(buf_size,GFP_KERNEL,&globalfifo_devp->mem_lock);
  if(!globalfifo_devp->memfifo)
  {
       ret = -ENOMEM;
       goto fail_malloc;
       
  }

  //just for a simple test
  int temp=10;
  int b;
  
  kfifo_put(globalfifo_devp->memfifo,&temp,sizeof(temp));
  kfifo_get(globalfifo_devp->memfifo,&b,sizeof(b));
  printk(KERN_ALERT "get %d ",b);
  
  //test end;
  
  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}


/*模块卸载函数*/
void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev);   /*注销cdev*/
  kfifo_free(globalfifo_devp->memfifo); //释放缓冲
  kfree(globalfifo_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/
}

MODULE_AUTHOR("ruanbanyong");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit);
